Skip Headers

Oracle® Call Interface Programmer's Guide, 10g Release 2 (10.2) Part Number B14250-02 |
Home | Book List | Contents | Index | Master Index | Feedback

Previous | Next
| View PDF

# 4 Using SQL Statements in OCI

This chapter discusses the concepts and steps involved in processing SQL statements with the Oracle Call Interface.

This chapter contains these topics:

- Overview of SQL Statement Processing

- Preparing Statements

- Binding Placeholders in OCI

- Executing Statements

- Describing Select-list Items

- Defining Output Variables in OCI

- Fetching Results

- Scrollable Cursors in OCI

## Overview of SQL Statement Processing

Chapter 2, "OCI Programming Basics" discussed the basic steps involved in any OCI application. This chapter presents a more detailed look at the specific tasks involved in processing SQL statements in an OCI program.

One of the most common tasks of an OCI program is to accept and process SQL statements. This section outlines the specific steps involved in this processing.

Once you have allocated the necessary handles and connected to a server, follow the steps illustrated in Figure 4-1, "Steps In Processing SQL Statements":

Figure 4-1 Steps In Processing SQL Statements

Description of "Figure 4-1 Steps In Processing SQL Statements"

1. Prepare statement. Define an application request using `OCIStmtPrepare()` or `OCIStmtPrepare2()`.

2. Bind placeholders, if necessary.. For DML statements and queries with input variables, perform one or more bind calls using

   - `OCIBindByPos()`

   - `OCIBindByName()`

   - `OCIBindObject()`

   - `OCIBindDynamic()`

   - `OCIBindArrayOfStruct()`

   to bind the address of each input variable (or PL/SQL output variable) or array to each placeholder in the statement.

3. A statement can also be prepared for execution with `OCIStmtPrepare2()`, an enhanced version of `OCIStmtPrepare()` introduced to support statement caching.

4. Execute. Call `OCIStmtExecute()` to execute the statement. For DDL statements, no further steps are necessary.

5. Describe, if necessary. Describe the select-list items, if necessary, using `OCIParamGet()` and `OCIAttrGet()`. This is an optional step; it is not required if the number of select-list items and the attributes of each item (such as its length and datatype) are known at compile time.

6. Define, if necessary. For queries, perform one or more define calls to `OCIDefineByPos()`, `OCIDefineObject()`, `OCIDefineDynamic()`, or `OCIDefineArrayOfStruct()` to define an output variable for each select-list item in the SQL statement. Note that you do not use a define call to define the output variables in an anonymous PL/SQL block. You have done this when you have bound the data.

7. Fetch, if necessary. For queries, call `OCIStmtFetch()` to fetch the results of the query.

Following these steps, the application can free allocated handles and then detach from the server, or it may process additional statements.

7.x Upgrade Note: OCI programs no longer require an explicit parse step. If a statement must be parsed, that step takes place upon execution. This means that 8.0 or later applications must issue an execute command for both DML and DDL statements.

For each of the steps in the diagram, the corresponding OCI function calls are listed. In some cases multiple calls may be required.

Each step is described in detail in the following sections.

Note: Some variation in the order of steps is possible. For example, it is possible to do the define step before the execute if the datatypes and lengths of returned values are known at compile time.

Additional steps beyond those listed earlier may be required if your application needs to do any of the following:

- initiate and manage multiple transactions

- manage multiple threads of execution

- perform piecewise inserts, updates, or fetches

  See Also: "Statement Caching in OCI"

## Preparing Statements

SQL and PL/SQL statements are prepared for execution by using the statement prepare call and any necessary bind calls. In this phase, the application specifies a SQL or PL/SQL statement and binds associated placeholders in the statement to data for execution. The client-side library allocates storage to maintain the statement prepared for execution.

An application requests a SQL or PL/SQL statement to be prepared for execution using the `OCIStmtPrepare()` call and passes to it a previously allocated statement handle. This is a completely local call, requiring no round trip to the server. No association is made between the statement and a particular server at this point.

Following the request call, an application can call `OCIAttrGet()` on the statement handle, passing `OCI_ATTR_STMT_TYPE` to the `attrtype` parameter, to determine what type of SQL statement was prepared. The possible attribute values and corresponding statement types are listed in Table 4-1, "OCI_ATTR_STMT_TYPE Values and Statement Types".

Table 4-1 OCI_ATTR_STMT_TYPE Values and Statement Types

Attribute Value | Statement Type
--- | ---
`OCI_STMT_SELECT` | SELECT statement
`OCI_STMT_UPDATE` | UPDATE statement
`OCI_STMT_DELETE` | DELETE statement
`OCI_STMT_INSERT` | INSERT statement
`OCI_STMT_CREATE` | CREATE statement
`OCI_STMT_DROP` | DROP statement
`OCI_STMT_ALTER` | ALTER statement
`OCI_STMT_BEGIN` | BEGIN... (PL/SQL)
`OCI_STMT_DECLARE` | DECLARE... (PL/SQL)

See Also: "Using PL/SQL in an OCI Program" "OCIStmtPrepare()"

### Using Prepared Statements on Multiple Servers

A prepared application request can be executed on multiple servers at run time by reassociating the statement handle with the respective service context handles for the servers. All information about the current service context and statement handle association is lost when a new association is made.

For example, consider an application such as a network manager, which manages multiple servers. In many cases, it is likely that the same `SELECT` statement will need to be executed against multiple servers to retrieve information for display. The OCI allows the network manager application to prepare a `SELECT` statement once and execute it against multiple servers. It must fetch all of the required rows from each server prior to reassociating the prepared statement with the next server.

Note: If a prepared statement must be re-executed frequently on the same server, it is more efficient to prepare a new statement for another service context.

## Binding Placeholders in OCI

Most DML statements, and some queries (such as those with a `WHERE` clause), require a program to pass data to Oracle as part of a SQL or PL/SQL statement. This data can be constant or literal, known when your program is compiled. For example, the following SQL statement, which adds an employee to a database contains several literals, such as 'BESTRY' and 2365:

    INSERT INTO emp VALUES
        (2365, 'BESTRY', 'PROGRAMMER', 2000, 20)

Coding a statement like this into an application would severely limit its usefulness. You would need to change the statement and recompile the program each time you add a new employee to the database. To make the program more flexible, you can write the program so that a user can supply input data at run time.

When you prepare a SQL statement or PL/SQL block that contains input data to be supplied at run time, placeholders in the SQL statement or PL/SQL block mark where data must be supplied. For example, the following SQL statement contains five placeholders, indicated by the leading colons (`:ename`), that show where input data must be supplied by the program.

    INSERT INTO emp VALUES
        (:empno, :ename, :job, :sal, :deptno)

You can use placeholders for input variables in any `DELETE`, `INSERT`, `SELECT`, or `UPDATE` statement, or a PL/SQL block, in any position in the statement where you can use an expression or a literal value. In PL/SQL, placeholders can also be used for output variables.

Placeholders cannot be used to represent other Oracle objects such as tables. For example, the following is not a valid use of the `emp` placeholder:

    INSERT INTO :emp VALUES
        (12345, 'OERTEL', 'WRITER', 50000, 30)

For each placeholder in a SQL statement or PL/SQL block, you must call an OCI routine that binds the address of a variable in your program to that placeholder. When the statement executes, Oracle gets the data that your program placed in the input, or bind, variables and passes it to the server with the SQL statement.

Binding is used for both input and output variables in non-query operations. In the following example,

    empno_out, ename_out, job_out, sal_out, and deptno_out

should be bound. These are outbinds (as opposed to regular inbinds).

    INSERT INTO emp VALUES
             (:empno, :ename, :job, :sal, :deptno)
          RETURNING
             (empno, ename, job, sal, deptno)
          INTO
             (:empno_out, :ename_out, :job_out, :sal_out, :deptno_out)

See Also: For detailed information about implementing bind operations, refer to Chapter 5, "Binding and Defining in OCI"

## Executing Statements

An OCI application executes prepared statements individually using `OCIStmtExecute()`.

When an OCI application executes a query, it receives data from Oracle that matches the query specifications. Within the database, the data is stored in Oracle-defined formats. When the results are returned, the OCI application can request that data be converted to a particular host language format, and stored in a particular output variable or buffer.

For each item in the select-list of a query, the OCI application must define an output variable to receive the results of the query. The define step indicates the address of the buffer and the type of the data to be retrieved.

Note: If output variables are defined for a `SELECT` statement before a call to `OCIStmtExecute()`, the number of rows specified by the `iters` parameter are fetched directly into the defined output buffers and additional rows equivalent to the prefetch count are prefetched. If there are no additional rows, then the fetch is complete without calling `OCIStmtFetch()`.

For non-queries, the number of times the statement is executed during array operations is equal to `iters - rowoff`, where `rowoff` is the offset in the bound array, and is also a parameter of the `OCIStmtExecute()` call.

For example, if an array of 10 items is bound to a placeholder for an `INSERT` statement, and `iters` is set to 10, all 10 items will be inserted in a single execute call when `rowoff` is zero. If `rowoff` is set to 2, only 8 items will be inserted.

See Also: "Defining Output Variables in OCI" for more information about defining output variables

### Execution Snapshots

The `OCIStmtExecute()` call provides the ability to ensure that multiple service contexts operate on the same consistent snapshot of the database's committed data. This is achieved by taking the contents of the `snap_out` parameter of one `OCIStmtExecute()` call and passing that value as the `snap_in` parameter of the next `OCIStmtExecute()` call.

Note: Uncommitted data in one service context is not visible to another context, even when using the same snapshot.

The datatype of both the `snap_out` and `snap_in` parameter is `OCISnapshot`, an OCI snapshot descriptor that is allocated with the `OCIDescAlloc()` function.

See Also: "OCI Descriptors"

It is not necessary to specify a snapshot when calling `OCIStmtExecute()`. The following sample code shows a basic execution in which the `snapsho`t parameters are passed as `NULL`.

    checkerr(errhp, OCIStmtExecute(svchp, stmthp, errhp, (ub4) 1, (ub4) 0,
              (OCISnapshot *)NULL, (OCISnapshot *) NULL, OCI_DEFAULT));

Note: The `checkerr()` function, which is user-developed, evaluates the return code from an OCI application.

### Execution Modes of OCIStmtExecute()

You can specify several modes for the `OCIStmtExecute()`call.

See Also: "OCIStmtExecute()" for the values of the parameter `mode`

#### Batch Error Mode

OCI provides the ability to perform array DML operations. For example, an application can process an array of `INSERT`, `UPDATE`, or `DELETE` statements with a single statement execution. If one of the operations fails due to an error from the server, such as a unique constraint violation, the array operation aborts and OCI returns an error. Any rows remaining in the array are ignored. The application must then re-execute the remainder of the array, and go through the whole process again if it encounters more errors, which makes additional round trips.

To facilitate processing of array DML operations, OCI provides the batch error mode (also called the enhanced DML array feature). This mode, which is specified in the `OCIStmtExecute()` call, simplifies DML array processing in the event of one or more errors. In this mode, OCI attempts to `INSERT`, `UPDATE`, or `DELETE` all rows, and collects information about any errors that occurred. The application can then retrieve error information and re-execute any DML operations which failed during the first call.In this way, all DML operations in the array are attempted in the first call, and any failed operations can be reissued in a second call.

Note: This feature is only available to applications linked with the 8.1 or later OCI libraries running against a release 8.1 or later server. Applications must also be re-coded to account for the new program logic described in this section.

This mode is used as follows:

1. The user specifies `OCI_BATCH_ERRORS` as the mode parameter of the `OCIStmtExecute()` call.

2. After performing an array DML operation with `OCIStmtExecute()`, the application can retrieve the number of errors encountered during the operation by calling `OCIAttrGet()` on the statement handle to retrieve the `OCI_ATTR_NUM_DML_ERRORS` attribute. For example:

       ub4   num_errs;
       OCIAttrGet(stmtp, OCI_HTYPE_STMT, &num_errs, 0, OCI_ATTR_NUM_DML_ERRORS,
                  errhp);

3. The application extracts each error using `OCIParamGet()`, along with its row information, from the error handle that was passed to the `OCIStmtExecute()` call. In order to retrieve the information, the application must allocate an additional new error handle for the `OCIParamGet()` call, populating the new error handle with batched error information. The application obtains the syntax of each error with `OCIErrorGet()`, and the row offset into the DML array at which the error occurred, by calling `OCIAttrGet()` on the new error handle.

   For example, once the `num_errs` amount has been retrieved, the application can issue the following calls:

       OCIError errhndl, errhp2;
       for (i=0; i<num_errs; i++)
       {
          OCIParamGet(errhp, OCI_HTYPE_ERROR, errhp2, (dvoid **)&errhndl, i);
          OCIAttrGet(errhndl, OCI_HTYPE_ERROR, &row_offset, 0,
              OCI_ATTR_DML_ROW_OFFSET, errhp2);
          OCIErrorGet(..., errhndl, ...);

   Following this, the application can correct the bind information for the appropriate entry in the array using the diagnostic information retrieved from the batched error. Once the appropriate bind buffers are corrected or updated, the application can re-execute the associated DML statements.

   Since it cannot be determined at compile time which rows in the first execution will cause errors, the binds for the subsequent DML should be done dynamically by passing in the appropriate buffers at runtime. The bind buffers used in the array binds done on the first DML operation can be reused.

#### Example of Batch Error Mode

The following code shows an example of how this execution mode might be used. In this example assume that we have an application which inserts five rows (with two columns, of types `NUMBER` and `CHAR`) into a table. Furthermore, let us assume only two rows (say, 1 and 3) are successfully inserted in the initial DML operation. The user then proceeds to correct the data (wrong data was being inserted the first time) and to issue an update with the corrected data. The user uses statement handles `stmtp1` and `stmtp2` to issue the `INSERT` and `UPDATE` respectively.

    OCIBind *bindp1[2], *bindp2[2];
    ub4 num_errs, row_off[MAXROWS], number[MAXROWS] = {1,2,3,4,5};
    char grade[MAXROWS] = {'A','B','C','D','E'};
    OCIError *errhp2;
    OCIError *errhndl[MAXROWS];
    ...
    /* Array bind all the positions */
    OCIBindByPos (stmtp1,&bindp1[0],errhp,1,(dvoid *)&number[0],
         sizeof(number[0]),SQLT_INT,(dvoid *)0, (ub2 *)0,(ub2 *)0,
                0,(ub4 *)0,OCI_DEFAULT);
    OCIBindByPos (stmtp1,&bindp1[1],errhp,2,(dvoid *)&grade[0],
         sizeof(grade[0]),SQLT_CHR,(dvoid *)0, (ub2 *)0,(ub2 *)0,0,
                (ub4 *)0,OCI_DEFAULT);
    /* execute the array INSERT */
    OCIStmtExecute (svchp,stmtp1,errhp,5,0,0,0,OCI_BATCH_ERRORS);
    /* get the number of errors, a different error handler errhp2 is used so that
     * the state of errhp is not changed */
    OCIAttrGet (stmtp1, OCI_HTYPE_STMT, &num_errs, 0,
                OCI_ATTR_NUM_DML_ERRORS, errhp2);
    if (num_errs) {
       /* The user can do one of two things: 1) Allocate as many  */
       /* error handles as number of errors and free all handles  */
       /* at a later time; or 2) Allocate one err handle and reuse */
       /* the same handle for all the errors */
       for (i = 0; i < num_errs; i++) {
          OCIHandleAlloc( (dvoid *)envhp, (dvoid **)&errhndl[i],
          (ub4) OCI_HTYPE_ERROR, 0, (dvoid *) 0);
          OCIParamGet(errhp, OCI_HTYPE_ERROR, errhp2, &errhndl[i], i);
          OCIAttrGet (errhndl[i], OCI_HTYPE_ERROR, &row_off[i], 0,
                      OCI_ATTR_DML_ROW_OFFSET, errhp2);
          /* get server diagnostics */
          OCIErrorGet (..., errhndl[i], ...);
          }
       }
    /* make corrections to bind data */
    OCIBindByPos (stmtp2,&bindp2[0],errhp,1,(dvoid *)0,sizeof(grade[0]),SQLT_INT,
         (dvoid *)0, (ub2 *)0,(ub2 *)0,0,(ub4 *)0,OCI_DATA_AT_EXEC);
    OCIBindByPos (stmtp2,&bindp2[1],errhp,2,(dvoid *)0,sizeof(number[0]),SQLT_DAT,
         (dvoid *)0, (ub2 *)0,(ub2 *)0,0,(ub4 *)0,OCI_DATA_AT_EXEC);
    /* register the callback for each bind handle, row_off and position
     * information can be passed to call back function by means of context
     * pointers.
     */
    OCIBindDynamic (bindp2[0],errhp,ctxp1,my_callback,0,0);
    OCIBindDynamic (bindp2[1],errhp,ctxp2,my_callback,0,0);
    /* execute the UPDATE statement */
    OCIStmtExecute (svchp,stmtp2,errhp,num_errs,0,0,0,OCI_BATCH_ERRORS);
    ...

In this example, `OCIBindDynamic()` is used with a callback because the user does not know at compile time what rows will return with errors. With a callback, you can simply pass the erroneous row numbers, stored in `row_off`, through the callback context and send only those rows that need to be updated or corrected. The same bind buffers can be shared between the `INSERT` and the `UPDATE` executes.

## Describing Select-list Items

If your OCI application is processing a query, you may need to obtain more information about the items in the select-list. This is particularly true for dynamic queries whose contents are not known until run time. In this case, the program may need to obtain information about the datatypes and column lengths of the select-list items. This information is necessary to define output variables that will receive query results.

For example, consider a query where the program has no prior information about the columns in the `employees` table:

    SELECT * FROM employees

There are two types of describes available: implicit and explicit.

An implicit describe is one that does not require any special calls to retrieve describe information from the server, although special calls are necessary to access the information. An implicit describe allows an application to obtain select-list information as an attribute of the statement handle after a statement has been executed without making a specific describe call. It is called implicit, because no describe call is required. The describe information comes free with the execute.

An explicit describe is one which requires the application to call a particular function to bring the describe information from the server. An application may describe a select-list (query) either implicitly or explicitly. Other schema elements must be described explicitly.

You can describe a query explicitly prior to execution. To do this, specify `OCI_DESCRIBE_ONLY` as the mode of `OCIStmtExecute()`, which does not execute the statement, but returns the select-list description. For performance reasons it is recommended that applications take advantage of the implicit describe that comes free with a standard statement execution.

An explicit describe with the `OCIDescribeAny()` call obtains information about schema objects rather than select-lists.

In all cases, the specific information about columns and datatypes is retrieved by reading handle attributes.

See Also: For information about using `OCIDescribeAny()` to obtain metadata pertaining to schema objects, refer to Chapter 6, "Describing Schema Metadata"

### Implicit Describe

After a SQL statement is executed, information about the select-list is available as an attribute of the statement handle. No explicit describe call is needed.

To retrieve information about multiple select-list items, an application can call `OCIParamGet()` with the pos parameter set to 1 the first time, and then iterate the value of pos and repeat the `OCIParamGet()` call until `OCI_ERROR` with ORA-24334 is returned. An application could also specify any position n to get a column at random.

Once a parameter descriptor has been allocated for a position in the select-list, the application can retrieve specific information by calling `OCIAttrGet()` on the parameter descriptor. Information available from the parameter descriptor includes the datatype and maximum size of the parameter.

The following sample code shows a loop that retrieves the column names and datatypes corresponding to a query following query execution. The query was associated with the statement handle by a prior call to `OCIStmtPrepare()`.

    ...
    OCIParam     *mypard = (OCIParam *) 0;
    ub2          dtype;
    text         *col_name;
    ub4          counter, col_name_len, char_semantics;
    ub2          col_width;
    sb4          parm_status;

    text *sqlstmt = (text *)"SELECT * FROM employees WHERE employee_id = 100";

    checkerr(errhp, OCIStmtPrepare(stmthp, errhp, (OraText *)sqlstmt,
                        (ub4)strlen((char *)sqlstmt),
                        (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT));

    checkerr(errhp, OCIStmtExecute(svchp, stmthp, errhp, 0, 0, (OCISnapshot *)0, (OCISnapshot *)0, OCI_DEFAULT));

    /* Request a parameter descriptor for position 1 in the select-list */
    counter = 1;
    parm_status = OCIParamGet((dvoid *)stmthp, OCI_HTYPE_STMT, errhp,
                   (dvoid **)&mypard, (ub4) counter);

    /* Loop only if a descriptor was successfully retrieved for
       current position, starting at 1 */

    while (parm_status == OCI_SUCCESS) {
       /* Retrieve the datatype attribute */
       checkerr(errhp, OCIAttrGet((dvoid*) mypard, (ub4) OCI_DTYPE_PARAM,
               (dvoid*) &dtype,(ub4 *) 0, (ub4) OCI_ATTR_DATA_TYPE,
               (OCIError *) errhp  ));

       /* Retrieve the column name attribute */
       col_name_len = 0;
       checkerr(errhp, OCIAttrGet((dvoid*) mypard, (ub4) OCI_DTYPE_PARAM,
               (dvoid**) &col_name, (ub4 *) &col_name_len, (ub4) OCI_ATTR_NAME,
               (OCIError *) errhp ));

       /* Retrieve the length semantics for the column */
       char_semantics = 0;
       checkerr(errhp, OCIAttrGet((dvoid*) mypard, (ub4) OCI_DTYPE_PARAM,
               (dvoid*) &char_semantics,(ub4 *) 0, (ub4) OCI_ATTR_CHAR_USED,
               (OCIError *) errhp  ));
       col_width = 0;
       if (char_semantics)
           /* Retrieve the column width in characters */
           checkerr(errhp, OCIAttrGet((dvoid*) mypard, (ub4) OCI_DTYPE_PARAM,
                   (dvoid*) &col_width, (ub4 *) 0, (ub4) OCI_ATTR_CHAR_SIZE,
                   (OCIError *) errhp  ));
       else
           /* Retrieve the column width in bytes */
           checkerr(errhp, OCIAttrGet((dvoid*) mypard, (ub4) OCI_DTYPE_PARAM,
                   (dvoid*) &col_width,(ub4 *) 0, (ub4) OCI_ATTR_DATA_SIZE,
                   (OCIError *) errhp  ));

       /* increment counter and get next descriptor, if there is one */
       counter++;
       parm_status = OCIParamGet((dvoid *)stmthp, OCI_HTYPE_STMT, errhp,
              (dvoid **)&mypard, (ub4) counter);
    } /* while */
    ...

The `checkerr()` function is used for error handling. The complete listing can be found in the first sample application in Appendix B, "OCI Demonstration Programs".

The calls to `OCIAttrGet()` and `OCIParamGet()` are local calls that do not require a network round trip, because all of the select-list information is cached on the client side after the statement is executed.

See Also: "OCIParamGet()" and. "OCIAttrGet()" "Parameter Attributes" for a list of the specific attributes of the parameter descriptor which may be read by OCIAttrGet().

### Explicit Describe of Queries

You can describe a query explicitly prior to execution. To do this, specify `OCI_DESCRIBE_ONLY` as the mode of `OCIStmtExecute()`; this does not execute the statement, but returns the select-list description.

Note: To maximize performance, it is recommended that applications execute the statement in default mode and use the implicit describe that accompanies the execution.

The following code demonstrates the use of explicit describe in a select-list to return information about columns.

    ...
    int i = 0;
    ub4 numcols = 0;
    ub2 type = 0;
    OCIParam *colhd = (OCIParam *) 0;   /* column handle */

    text *sqlstmt = (text *)"SELECT * FROM employees WHERE employee_id = 100";

    checkerr(errhp, OCIStmtPrepare(stmthp, errhp, (OraText *)sqlstmt,
                        (ub4)strlen((char *)sqlstmt),
                        (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT));

    /* initialize svchp, stmhp, errhp, rowoff, iters, snap_in, snap_out */
    /* set the execution mode to OCI_DESCRIBE_ONLY. Note that setting the mode to
    OCI_DEFAULT does an implicit describe of the statement in addition to executing
    the statement */

    checkerr(errhp, OCIStmtExecute(svchp, stmthp, errhp, 0, 0,
            (OCISnapshot *) 0, (OCISnapshot *) 0, OCI_DESCRIBE_ONLY));

    /* Get the number of columns in the query */
    checkerr(errhp, OCIAttrGet((dvoid *)stmthp, OCI_HTYPE_STMT, (dvoid *)&numcols,
                          (ub4 *)0, OCI_ATTR_PARAM_COUNT, errhp));

    /* go through the column list and retrieve the datatype of each column. We
    start from pos = 1 */
    for (i = 1; i <= numcols; i++)
    {
      /* get parameter for column i */
      checkerr(errhp, OCIParamGet((dvoid *)stmthp, OCI_HTYPE_STMT, errhp, (dvoid **)&colhd, i));

      /* get data-type of column i */
      type = 0;
      checkerr(errhp, OCIAttrGet((dvoid *)colhd, OCI_DTYPE_PARAM,
              (dvoid *)&type, (ub4 *)0, OCI_ATTR_DATA_TYPE, errhp));
    }
    ...

## Defining Output Variables in OCI

Query statements return data from the database to your application. When processing a query, you must define an output variable or an array of output variables for each item in the select-list that you want to retrieve data from. The define step creates an association that determines where returned results are stored, and in what format.

For example, to process the following statement you would normally need to define two output variables, one to receive the value returned from the `name` column, and one to receive the value returned from the `ssn` column:

    SELECT name, ssn FROM employees
        WHERE empno = :empnum

See Also: Chapter 5, "Binding and Defining in OCI"

## Fetching Results

If an OCI application has processed a query, it is typically necessary to fetch the results with `OCIStmtFetch()` or with `OCIStmtFetch2()` after the statement has completed execution. Oracle encourages the use of `OCIStmtFetch2()`, which supports scrollable cursors.

See Also: "Scrollable Cursors in OCI"

Fetched data is retrieved into output variables that have been specified by define operations.

Note: If output variables are defined for a `SELECT` statement before a call to `OCIStmtExecute()`, the number of rows specified by the `iters` parameter is fetched directly into the defined output buffers

See Also: These statements fetch data associated with the sample code in the section "Steps Used in OCI Defining". Refer to that example for more information. For information about defining output variables, see the section "Overview of Defining in OCI".

### Fetching LOB Data

If LOB columns or attributes are part of a select-list, they can be returned as LOB locators or actual LOB values, depending on how you define them. If LOB locators are fetched, then the application can perform further operations on these locators through the `OCILobXXX` interfaces.

See Also: Chapter 7, "LOB and BFILE Operations", for more information about working with LOB locators in the OCI. "Defining LOB Output Variables" for usage and examples of selecting LOB data without the use of locators.

### Setting Prefetch Count

In order to minimize server round trips and optimize the performance, the OCI can prefetch result set rows when executing a query. You can customize this prefetching by setting either the `OCI_ATTR_PREFETCH_ROWS` or `OCI_ATTR_PREFETCH_MEMORY` attribute of the statement handle using the `OCIAttrSet()` function. These attributes are used as follows:

- `OCI_ATTR_PREFETCH_ROWS` sets the number of rows to be prefetched. If it is not set, then the default value is 1. If the `iters` parameter of `OCIStmtExecute()` is 0 and prefetching is enabled, the rows are buffered during calls to `OCIStmtFetch2()`. The prefetch value can be altered after execution and between fetches.

- `OCI_ATTR_PREFETCH_MEMORY` sets the memory allocated for rows to be prefetched. The application then fetches as many rows as will fit into that much memory.

When both of these attributes are set, the OCI prefetches rows up to the `OCI_ATTR_PREFETCH_ROWS` limit unless the `OCI_ATTR_PREFETCH_MEMORY` limit is reached, in which case the OCI returns as many rows as will fit in a buffer of size `OCI_ATTR_PREFETCH_MEMORY`.

By default, prefetching is turned on, and the OCI fetches an extra row all the time. To turn prefetching off, set both the `OCI_ATTR_PREFETCH_ROWS` and `OCI_ATTR_PREFETCH_MEMORY` attributes to zero.

Note: Prefetching is not in effect if `LONG` columns are part of the query. Queries containing LOB columns can be prefetched, because the LOB locator, not the data, is returned by the query.

See Also: "Statement Handle Attributes".

## Scrollable Cursors in OCI

A cursor is a current position in a result set. Execution of a cursor puts the results of the query into a set of rows called the result set that can be fetched either sequentially or non-sequentially. In the latter case the cursor is known as a scrollable cursor.

A scrollable cursor provides support for forward and backward access into the result set from a given position, using either absolute or relative row number offsets into the result set.

Rows are numbered starting at one. For a scrollable cursor, you can fetch previously-fetched rows, the n-th row in the result set, or the n-th row from the current position. Client-side caching of either the partial or entire result set improves performance by limiting calls to the server.

Oracle does not support DML operations on scrollable cursors. A cursor cannot be made scrollable if the LONG datatype is part of the select list.

Moreover, fetches from a scrollable statement handle are based on the snapshot at execution time. OCI client prefetching works with OCI scrollable cursors. The size of the client prefetch cache can be controlled by the existing OCI attributes `OCI_ATTR_PREFETCH_ROWS` and `OCI_ATTR_PREFETCH_MEMORY`.

Note: Do not use scrollable cursors unless you require the functionality, because they use more server resources and can have greater response times than non-scrollable cursors.

The `OCIStmtExecute()` call has an execution mode for scrollable cursors, `OCI_STMT_SCROLLABLE_READONLY`. The default for statement handles is non-scrollable, forward sequential access only, where the mode is `OCI_FETCH_NEXT`. You must set this execution mode each time the statement handle is executed.

The statement handle attribute `OCI_ATTR_CURRENT_POSITION` can be retrieved using `OCIAttrGet()` only. This attribute cannot be set by the application; it indicates the current position in the result set.

For non-scrollable cursors, `OCI_ATTR_ROW_COUNT` is the total number of rows fetched into user buffers with the `OCIStmtFetch2()` calls since this statement handle was executed. Since non-scrollable cursors are forward sequential only, `OCI_ATTR_ROW_COUNT` also represents the highest row number seen by the application.

For scrollable cursors, `OCI_ATTR_ROW_COUNT` will represent the maximum (absolute) row number fetched into the user buffers. Since the application can arbitrarily position the fetches, this does not have to be the total number of rows fetched into the your buffers since the (scrollable) statement was executed.

The attribute `OCI_ATTR_ROWS_FETCHED` on the statement handle, represents the number of rows that were successfully fetched into the user's buffers in the last fetch call or execute. It works for both scrollable and non-scrollable cursors.

Use the OCIStmtFetch2() call, instead of the `OCIStmtFetch()` call, which is retained for backward compatibility. You are encouraged to use `OCIStmtFetch2()`, for all new applications, even those not using scrollable cursors. This call also works for non-scrollable cursors, but can raise an error if any other orientation besides `OCI_DEFAULT` or `OCI_FETCH_NEXT` is passed.

Scrollable cursors are supported for remote mapped queries. Transparent application Failover (TAF) is supported for scrollable cursors.

Note: If you call `OCIStmtFetch2()` with the `nrows` parameter set to 0, the cursor is cancelled.

See Also: "OCIStmtFetch2()" "Setting Prefetch Count"

### Increasing Scrollable Cursor Performance

Response time is improved if you use OCI client-side prefetch buffers. After calling `OCIStmtExecute()` for a scrollable cursor, call `OCIStmtFetch2()` using `OCI_FETCH_LAST` to obtain the size of the result set. Then set `OCI_ATTR_PREFETCH_ROWS` to about 20% of that size, and set `OCI_PREFETCH_MEMORY` if the result set uses a large amount of memory.

### Example of Access on a Scrollable Cursor

Assume that a result set is returned by the SQL query:

    SELECT empno, ename FROM emp

and that the table `EMP` has 14 rows. One use of scrollable cursors is:

    ...
    /* execute the scrollable cursor in the scrollable mode */
    OCIStmtExecute(svchp, stmthp, errhp, (ub4)0, (ub4)0, (CONST OCISnapshot *)NULL,
                   (OCISnapshot *) NULL, OCI_STMT_SCROLLABLE_READONLY );

    /* Fetches rows with absolute row numbers 6, 7, 8. After this call,
     OCI_ATTR_CURRENT_POSITION = 8, OCI_ATTR_ROW_COUNT = 8 */
    checkprint(errhp, OCIStmtFetch2(stmthp, errhp, (ub4) 3,
                                   OCI_FETCH_ABSOLUTE, (sb4) 6, OCI_DEFAULT);

    /* Fetches rows with absolute row numbers 6, 7, 8. After this call,
     OCI_ATTR_CURRENT_POSITION = 8, OCI_ATTR_ROW_COUNT = 8 */
    checkprint(errhp, OCIStmtFetch2(stmthp, errhp, (ub4) 3,
                                   OCI_FETCH_RELATIVE, (sb4) -2, OCI_DEFAULT);

    /* Fetches rows with absolute row numbers 14. After this call,
     OCI_ATTR_CURRENT_POSITION = 14, OCI_ATTR_ROW_COUNT = 14 */
    checkprint(errhp, OCIStmtFetch2(stmthp, errhp, (ub4) 1,
                                   OCI_FETCH_LAST, (sb4) 0, OCI_DEFAULT);

    /* Fetches rows with absolute row number 1. After this call,
     OCI_ATTR_CURRENT_POSITION = 1, OCI_ATTR_ROW_COUNT = 14 */
    checkprint(errhp, OCIStmtFetch2(stmthp, errhp, (ub4) 1,
                                   OCI_FETCH_FIRST, (sb4) 0, OCI_DEFAULT);

    /* Fetches rows with absolute row numbers 2, 3, 4. After this call,
     OCI_ATTR_CURRENT_POSITION = 4, OCI_ATTR_ROW_COUNT = 14 */
    checkprint(errhp, OCIStmtFetch2(stmthp, errhp, (ub4) 3,
                                   OCI_FETCH_NEXT, (sb4) 0, OCI_DEFAULT);

    /* Fetches rows with absolute row numbers 3,4,5,6,7. After this call,
     OCI_ATTR_CURRENT_POSITION = 7, OCI_ATTR_ROW_COUNT = 14. It is assumed
    the user's define memory is allocated. */
    checkprint(errhp, OCIStmtFetch2(stmthp, errhp, (ub4) 5,
                                   OCI_FETCH_PRIOR, (sb4) 0, OCI_DEFAULT);
    ...
    }
    checkprint (errhp, status)
    {
     ub4 rows_fetched;
    /* This checks for any OCI errors before printing the results of the fetch call
       in the define buffers */
     checkerr (errhp, status);
     checkerr(errhp, OCIAttrGet((CONST void *) stmthp, OCI_HTYPE_STMT,
             (void *) &rows_fetched, (uint *) 0, OCI_ATTR_ROWS_FETCHED, errhp));
    }
    ...